package com.luxiu;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;

public abstract class FrameLess extends Application {
    protected Stage stage;
    protected Scene scene;
    private DropShadow dropshadow; // 窗口阴影
    protected GridPane titleBar; // 标题栏，拖拽区
    protected VBox parent;
    private BackFrontAnimation backFrontAnimation = new BackFrontAnimation();
    private Timer iconifiedTimer = new Timer();
    private Insets buttonInsets = new Insets(0, 7, 0, 7);
    private final double imageScale = .4;
    protected HBox winControlPanel = new HBox();
    protected Label closeControl;
    private Class self;

    private double sceneX;
    private double sceneY;
    private String imageDir = "/image"; // image 目录路径
    private double winWidth = 0; // 窗口宽度
    private double winHeight = 0; // 窗口高度
    private double preMaxWinWidth = 0; // 最大化之前width
    private double preMaxWinHeight = 0; //最大化之前height
    private final double bodyPadding = 5;
    private final double windowFocusedShadowRadius = 6; // 聚焦时窗口阴影 Radius
    private final double windowUnFocusedShadowRadius = 3;  // 非聚焦时窗口阴影 Radius
    private double titleBarHeight = 20;
    private long lastDragTime = System.currentTimeMillis(); // 上次拖拽时间

    private double beforeFadingWidth = 0;
    private double beforeFadingHeight = 0;
    private long lastMoveTime = System.currentTimeMillis();
    private double beforeResizeWinBottom = 0; // resize之前window底部距离
    private double beforeResizeWinRight = 0; // resize之前window右边距离
    private double minWidth = 700; // 窗口最小宽度
    private double minHeight = 420; // 窗口最小高度

    private boolean isLeft = false; // 鼠标在左边
    private boolean isRight = false;  // 鼠标在右边
    private boolean isTop = false; // 鼠标在顶部
    private boolean isBottom = false; // 鼠标在底部
    private boolean isDragging = false; // 是否在拖拽

    @Override
    public void init() {
        self = FrameLess.class;

        dropshadow = new DropShadow();// 阴影向外
        dropshadow.setRadius(windowFocusedShadowRadius);// 颜色蔓延的距离
        dropshadow.setColor(Color.GREY);// 设置颜色
        dropshadow.setBlurType(BlurType.THREE_PASS_BOX);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        stage = primaryStage;
        initFrame();
        scene = new Scene(parent, Color.TRANSPARENT);
        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(scene);

        initTitleBar();
        setFrameLess();
        setDraggable();
        setAdjustSize();
        setNotExceedTop();
        setWindowFocusStateShadow();
        // 初始化 winHeight、winWidth
        stage.setOnShowing(event -> {
            winHeight = stage.getHeight();
            winWidth = stage.getWidth();
        });
    }

    /**
     * 初始化 frame
     */
    private void initFrame() {
        titleBar = new GridPane();
        parent = new VBox(titleBar);

        parent.setAlignment(Pos.TOP_CENTER);
        titleBar.setAlignment(Pos.TOP_RIGHT);
        titleBar.setStyle("-fx-background-color: transparent;-fx-padding: 0 0 0 0");
        titleBar.setPrefHeight(titleBarHeight);
        titleBar.setMaxHeight(titleBarHeight);
    }

    /**
     * 设置 TitleBar control 样式
     *
     * @param label label
     */
    private void initTitleBarControl(Label label) {
        label.setMaxHeight(titleBarHeight);
        label.setPrefHeight(titleBarHeight);
        label.setStyle("-fx-background-color: transparent;");
        label.setPadding(buttonInsets);

        label.addEventHandler(MouseEvent.MOUSE_DRAGGED, Event::consume);
    }

    /**
     * 关闭
     *
     * @return Label
     */
    private Label getCloseControl() {
        ImageView closeBlackImg = new ImageView(new Image(self.getResourceAsStream(imageDir + "/close-black.png")));
        ImageView closeWhiteImg = new ImageView(new Image(self.getResourceAsStream(imageDir + "/close-white.png")));
        closeControl = new Label("", closeBlackImg);
        initTitleBarControl(closeControl);
        closeBlackImg.setScaleX(imageScale);
        closeBlackImg.setScaleY(imageScale);
        closeWhiteImg.setScaleX(imageScale);
        closeWhiteImg.setScaleY(imageScale);

        closeControl.setOnMouseEntered(event -> {
            closeControl.setStyle("-fx-background-color: #ff504a");
            closeControl.setGraphic(closeWhiteImg);
        });
        closeControl.setOnMouseExited(event -> {
            closeControl.setStyle("-fx-background-color: transparent");
            closeControl.setGraphic(closeBlackImg);
        });
        closeControl.setOnMouseClicked(event -> {
            // 左键，窗口正在loading则不处理
            if (event.getButton() == MouseButton.PRIMARY && !App.isFading) {
                exit();
            }
            event.consume();
        });

        return closeControl;
    }

    /**
     * 点击 CloseControl
     */
    protected void clickCloseControl() {
        exit();
    }

    private void exit() {
        // 清除定时器，防止关闭主窗口后程序未退出
        iconifiedTimer.cancel();
        stage.close();
    }

    /**
     * 最大化
     *
     * @return Label
     */
    private Label getMaxControl() {
        ImageView unMaximizedImg = new ImageView(new Image(self.getResourceAsStream(imageDir + "/unMaximized.png")));
        ImageView maximizedImg = new ImageView(new Image(self.getResourceAsStream(imageDir + "/maximized.png")));
        Label maxLabel = new Label("", unMaximizedImg);
        initTitleBarControl(maxLabel);
        unMaximizedImg.setScaleX(imageScale);
        unMaximizedImg.setScaleY(imageScale);
        maximizedImg.setScaleX(imageScale);
        maximizedImg.setScaleY(imageScale);

        maxLabel.setOnMouseEntered(event -> maxLabel.setStyle("-fx-background-color: #dfdfdf"));
        maxLabel.setOnMouseExited(event -> maxLabel.setStyle("-fx-background-color: transparent"));
        maxLabel.setOnMouseClicked(event -> {
            // 窗口正在loading则不处理
            if (event.getButton() == MouseButton.PRIMARY && !App.isFading) {
                // 退出最大化
                if (stage.isMaximized()) {
                    maxLabel.setGraphic(unMaximizedImg);
                    parent.setBackground(new Background(new BackgroundFill(parent.getBackground().getFills().get(0).getFill(), CornerRadii.EMPTY, new Insets(bodyPadding))));
                    parent.setPadding(new Insets(bodyPadding));
                    stage.setMaximized(false);
                    stage.setWidth(preMaxWinWidth);
                    stage.setHeight(preMaxWinHeight);
                } else {
                    maxLabel.setGraphic(maximizedImg);
                    parent.setBackground(new Background(new BackgroundFill(parent.getBackground().getFills().get(0).getFill(), CornerRadii.EMPTY, new Insets(0))));
                    parent.setPadding(new Insets(0));
                    preMaxWinWidth = stage.getWidth();
                    preMaxWinHeight = stage.getHeight();
                    stage.setMaximized(true);
                    stage.setX(0);
                    stage.setY(0);
                    Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
                    stage.setWidth(visualBounds.getWidth());
                    // 不覆盖任务栏
                    stage.setHeight(visualBounds.getHeight());
                }
                winHeight = stage.getHeight();
                winWidth = stage.getWidth();
                beforeFadingHeight = winHeight;
                beforeFadingWidth = winWidth;
            }
            event.consume();
        });

        return maxLabel;
    }

    /**
     * 最小化
     *
     * @return Label
     */
    private Label getMinControl() {
        ImageView minimizedImg = new ImageView(new Image(self.getResourceAsStream(imageDir + "/minimized.png")));
        Label minControl = new Label("", minimizedImg);
        initTitleBarControl(minControl);
        minimizedImg.setScaleX(imageScale);
        minimizedImg.setScaleY(imageScale);

        minControl.setOnMouseEntered(event -> minControl.setStyle("-fx-background-color: #dfdfdf"));
        minControl.setOnMouseExited(event -> minControl.setStyle("-fx-background-color: transparent"));
        minControl.setOnMouseClicked(event -> {
            // 窗口正在loading则不处理
            if (event.getButton() == MouseButton.PRIMARY && !App.isFading) {
                backFrontAnimation.toBack();
            }
            event.consume();
        });

        return minControl;
    }

    /**
     * 初始化 titleBar
     */
    private void initTitleBar() {
        Label maxControl = getMaxControl();
        Label minControl = getMinControl();
        Label closeControl = getCloseControl();
        winControlPanel.setStyle("-fx-background-color: transparent");
        winControlPanel.getChildren().add(minControl);
        winControlPanel.getChildren().add(maxControl);
        winControlPanel.getChildren().add(closeControl);
        // 双击触发最大化事件
        titleBar.setOnMouseClicked(event -> {
            if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() >= 2) {
                maxControl.fireEvent(event);
            }
        });

        titleBar.add(winControlPanel, 1, 0);
    }

    /**
     * 设置拖拽
     */
    private void setDraggable() {
        this.titleBar.setOnMousePressed(event -> {
            sceneX = event.getSceneX();
            sceneY = event.getSceneY();
        });

        this.titleBar.setOnMouseDragged(event -> {
            // 正在调整大小不可拖拽
            if (isLeft || isRight || isTop || isBottom) {
                return;
            }
            // 最大化时不可拖拽
            if (!stage.isMaximized()) {
                stage.setX(event.getScreenX() - sceneX);
                stage.setY(event.getScreenY() - sceneY);
            }
        });
    }

    /**
     * 设置无边框
     */
    private void setFrameLess() {
        parent.setPrefHeight(this.stage.getHeight());
        parent.setPrefHeight(this.stage.getWidth());
        parent.setEffect(dropshadow);
        parent.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, new Insets(bodyPadding))));
        parent.setPadding(new Insets(bodyPadding));
    }

    /**
     * 设置窗口大小调节
     */
    private void setAdjustSize() {
        parent.setOnMouseMoved(event -> {
            if (System.currentTimeMillis() - lastMoveTime < 250) {
                return;
            }
            lastMoveTime = System.currentTimeMillis();
            // 正在拖拽则不处理
            if (isDragging || stage.isMaximized()) {
                return;
            }

            winWidth = stage.getWidth();
            winHeight = stage.getHeight();

            double x = event.getSceneX();
            double y = event.getSceneY();

            Cursor cursor = Cursor.DEFAULT;

            isLeft = isRight = isBottom = isTop = false;

            if (x <= bodyPadding) {
                isLeft = true;
                cursor = Cursor.H_RESIZE;
            }
            if (y <= bodyPadding) {
                isTop = true;
                cursor = Cursor.V_RESIZE;
            }
            if (winWidth - x <= bodyPadding) {
                isRight = true;
                cursor = Cursor.H_RESIZE;
            }
            if (winHeight - y <= bodyPadding) {
                isBottom = true;
                cursor = Cursor.V_RESIZE;
            }

            if (isBottom && isRight) {
                cursor = Cursor.SE_RESIZE;
            } else if (isBottom && isLeft) {
                cursor = Cursor.SW_RESIZE;
            } else if (isTop && isRight) {
                cursor = Cursor.NE_RESIZE;
            } else if (isTop && isLeft) {
                cursor = Cursor.NW_RESIZE;
            }

            parent.setCursor(cursor);
        });

        parent.setOnMouseDragged(event -> {
            if (stage.isMaximized()) {
                return;
            }

            // 减小错位
            if (System.currentTimeMillis() - lastDragTime <= 15) {
                return;
            }
            lastDragTime = System.currentTimeMillis();

            winWidth = stage.getWidth();
            winHeight = stage.getHeight();
            double x = event.getSceneX();
            double y = event.getSceneY();

            if (isRight) {
                stage.setWidth(x);
            }
            if (isBottom) {
                stage.setHeight(y);
            }
            // 限制窗口大小
            if (stage.getWidth() <= minWidth) {
                stage.setWidth(minWidth);
            }
            if (stage.getHeight() <= minHeight) {
                stage.setHeight(minHeight);
            }

            if (isLeft) {
                stage.setWidth(winWidth - x);
                // 限制窗口大小
                if (x >= 0 && stage.getWidth() <= minWidth) {
                    stage.setWidth(minWidth);
                } else {
                    stage.setX(event.getScreenX());
                }
            }
            if (isTop) {
                stage.setHeight(winHeight - y);
                // 限制窗口大小
                if (x >= 0 && stage.getHeight() <= minHeight) {
                    stage.setHeight(minHeight);
                } else {
                    stage.setY(event.getScreenY());
                }
            }

            winWidth = stage.getWidth();
            winHeight = stage.getHeight();
            adjustWindowPosition();
        });

        parent.setOnMousePressed(event -> {
            isDragging = true;
            beforeResizeWinBottom = stage.getY() + winHeight;
            beforeResizeWinRight = stage.getX() + winWidth;
        });
        parent.setOnMouseReleased(event -> {
            isDragging = false;
            beforeFadingHeight = stage.getHeight();
            beforeFadingWidth = stage.getWidth();
            adjustWindowPosition();
        });
    }

    /**
     * 拖动 window 后调整 WindowPosition
     */
    private void adjustWindowPosition() {
        if (isLeft) {
            double x = stage.getX() + winWidth;
            double offsetX = (x - beforeResizeWinRight) / 2;
            stage.setX(stage.getX() - offsetX);
            stage.setWidth(stage.getWidth() - offsetX);
        }
        if (isTop) {
            double y = stage.getY() + winHeight;
            double offsetY = (y - beforeResizeWinBottom) / 2;
            stage.setY(stage.getY() - offsetY);
            stage.setHeight(stage.getHeight() - offsetY);
        }
    }

    /**
     * 设置窗口不超过顶部
     */
    private void setNotExceedTop() {
        parent.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> {
            if (stage.getY() < 0) {
                stage.setY(0);
            }
        });
    }

    private class BackFrontAnimation {

        private final int interval = 10;
        private final double step = 0.05;
        private int times = (int) (1 / step);
        private double perWidth;
        private double perHeight;
        private double width; // 当前窗口宽度
        private double height; // 当前窗口高度
        private double opacity; // 当前窗口不透明度
        private boolean isInitiated = false; // 是否已初始化 size

        // back to page
        void toFront() {

            CompletableFuture.runAsync(() -> {
                opacity = 0;
                width = 0;
                height = 0;

                while (opacity <= 1) {
                    try {
                        Thread.sleep(interval);
                    } catch (InterruptedException e) {
                        System.err.println(e.getMessage());
                    }

                    opacity += step;
                    width += perWidth;
                    height += perHeight;

                    Platform.runLater(() -> {
                        stage.setOpacity(Math.min(opacity, 1.0));
                        stage.setWidth(width);
                        stage.setHeight(height);
                    });
                }
                // 设置原来值
                Platform.runLater(() -> {
                    stage.setOpacity(1);
                    stage.setWidth(beforeFadingWidth);
                    stage.setHeight(beforeFadingHeight);
                });

            });
        }

        // minimize
        void toBack() {

            // 初始化 size
            if (!isInitiated && beforeFadingWidth == 0) {
                beforeFadingWidth = stage.getWidth();
                beforeFadingHeight = stage.getHeight();
                isInitiated = true;
            }

            CompletableFuture.runAsync(() -> {
                opacity = stage.getOpacity();
                width = beforeFadingWidth;
                height = beforeFadingHeight;
//                System.out.println("winHeight: " + winHeight + ", winWidth: " + winWidth);
                perWidth = width / times;
                perHeight = height / times;

                while (opacity >= 0) {
                    try {
                        Thread.sleep(interval);
                    } catch (InterruptedException e) {
                        System.err.println(e.getMessage());
                    }

                    opacity -= step;
                    width -= perWidth;
                    height -= perHeight;

                    Platform.runLater(() -> {
                        stage.setOpacity(Math.max(opacity, step));
                        stage.setWidth(width);
                        stage.setHeight(height);
                    });
                }

                Platform.runLater(() -> {
                    // 设置0值
                    stage.setIconified(true);
                    stage.setOpacity(0);
                    stage.setWidth(0);
                    stage.setHeight(0);

                    // 最小化后启动定时器
                    iconifiedTimer.schedule(new TimerTask() {
                        @Override
                        public void run() {
                            if (!stage.isIconified()) {
                                // 取消本次定时器
                                this.cancel();
                                backFrontAnimation.toFront();
                            }
                        }
                    }, 0, 100);
                });

            });
        }
    }

    /**
     * 设置焦点状态窗口阴影
     */
    private void setWindowFocusStateShadow() {
        // 监听窗口焦点
        stage.focusedProperty().addListener((observable, oldValue, newValue) -> {
            // 获取焦点
            if (observable.getValue()) {
                dropshadow.setRadius(windowFocusedShadowRadius);
            } else {
                dropshadow.setRadius(windowUnFocusedShadowRadius);
            }
        });
    }

    public double getMinWidth() {
        return minWidth;
    }

    public void setMinWidth(double minWidth) {
        this.minWidth = minWidth;
    }

    public double getMinHeight() {
        return minHeight;
    }

    public void setMinHeight(double minHeight) {
        this.minHeight = minHeight;
    }
}
